import settings from './settings';

/**
 * Internal class for handling the priority sorting of ticker handlers.
 *
 * @private
 * @class
 * @memberof PIXI.ticker
 */
class TickerRunner
{
    /**
     * Constructor
     *
     * @param {Function} fn - The listener function to be added for one update
     * @param {Object[]} [args=null] - The listener function's extends arguments
     * @param {boolean} [once=false] - If the handler should fire once
     */
    constructor(fn,args,once = false)
    {
        /**
         * The handler function to execute.
         * @member {Function}
         */
        this.fn = fn;

        /**
         * The function's arguments.
         * @member {number}
         */
        this.args = args;

        /**
         * If this should only execute once.
         * @member {boolean}
         */
        this.once = once;

        /**
         * The next item in chain.
         * @member {TickerListener}
         */
        this.next = null;

        /**
         * `true` if this listener has been stopped.
         * @member {boolean}
         * @private
         */
        this._stopped = false;

        /**
         * `true` if this listener has been stopped.
         * @member {number}
         * @private
         */
        this._speed = 1.0;
    }

    /**
     * 执行当前的逻辑过程.
     * @param {number} deltaTime - time since the last emit.
     * @return {boolean} 是否结束该执行过程
     */
    doit(deltaTime)
    {
        this.args[0] = deltaTime*this._speed;
        return this.fn.apply(this.fn.this,this.args) || this.once;        
    }

    /**
     * 停止当前该执行过程.
     */
    stop()
    {
        this._stopped = true;
    }

    /**
     * 是否已停止
     * @member {boolean}
     * @readonly
     */
    get stopped(){
        return this._stopped;
    }

    /**
     * 获取当前过程的执行速度比，默认为1，小于1时进行放慢操作，大于1时进行快进操作
     * @member {number}
     */
    get speed(){
        return this._speed;
    }

    /**
     * 设置当前过程的执行速度比，默认为1，小于1时进行放慢操作，大于1时进行快进操作
     * @member {number}
     */
    set speed(v){
        return this._speed=v;
    }
}


/**
 * A Ticker class that runs an update loop that other objects listen to.
 * This class is composed around listeners
 * meant for execution on the next requested animation frame.
 * Animation frames are requested only when necessary,
 * e.g. When the ticker is started and the emitter has listeners.
 *
 * @class
 * @memberof PIXI.ticker
 */
class Ticker
{
    /**
     *
     */
    constructor()
    {
        this._frame_time = 0;
        this._app_time = 0;
        this._last_frame_time = 0;
        this._frame_delay = 1000/60.0;
        this._fps_counter = 0;
        this._fps = 0;
        this._last_fps_time = 0;
        this._calls=null;

        /**
         * Internal tick method bound to ticker instance.
         * This is because in early 2015, Function.bind
         * is still 60% slower in high performance scenarios.
         * Also separating frame requests from update method
         * so listeners may be called at any time and with
         * any animation API, just invoke ticker.update(time).
         *
         * @private
         * @param {number} time - Time since last tick.
         */
        this._tick = (time) => {
            // Invoke listeners now
            this._update(time);
            // Listener side effects may have modified ticker state.
            if (!this._stopped && this._calls){
                this._requestId = requestAnimationFrame(this._tick);
            }
        };
    }

    /**
     * Starts the ticker. If the ticker has listeners
     * a new animation frame is requested at this point.
     */
    start()
    {
        this._stopped = false;
        if(this._calls)
            this._tick();
    }

    /**
     * Stops the ticker. If the ticker has requested
     * an animation frame it is canceled at this point.
     */
    stop()
    {
        this._stopped = true;
    }

    /**
     * Destroy the ticker and don't use after this. Calling
     * this method removes all references to internal events.
     */
    destroy()
    {
        this.stop();
        this._calls = null;
    }

    _update(nnow=performance.now()){
        //let nnow = time;//
        nnow = performance.now();
        let delay = nnow - this._last_frame_time;
        /*
        while(delay < this._frame_delay){
            return;
            nnow = performance.now();
            delay = nnow - this._last_frame_time;
        }
        */
        delay = delay < 0 || delay > 1000?1000:delay;
        this._frame_time = delay / 1000.0;
        if(nnow - this._last_fps_time >= 1000.0){
            this._fps = this._fps_counter;
            this._fps_counter = 0;
            this._last_fps_time = nnow;
        }else{
            this._fps_counter ++;
        }
        this._last_frame_time = nnow;
        this._app_time += this._frame_time;

        let c = this._calls;
        let pc = null;
        const delta_sec = this._frame_time;
        while(c){
            if(c.stopped || c.doit(delta_sec)){
                c = c.next;
                if(pc) pc.next = c;
                else this._calls=c;
            }else{
                pc = c;
                c = c.next;
            }
        }
    }

    /**
     * 运行一个逻辑回调，在逻辑主循环中调用.
     *
     * @param {Function} fn - 回调函数.原型为function(delta,...);如果返回true则表示立刻结束并移出该执行逻辑
     * @param {object} [params=null] - 其他参数，回调时会进行回传.
     * @returns {PIXI.ticker.TickerRunner} This instance of a tickerruner
     */
    run(fn,params)
    {
        return this._run(false,fn,arguments);
    }

    /**
     * 运行一个回调一次，运行之后自动移出
     *
     * @param {Function} fn - 回调函数.原型为function(delta,...);
     * @param {object} [params=null] - 其他参数，回调时会进行回传.
     * @returns {PIXI.ticker.TickerRunner} This instance of a tickerruner
     */
    run_once(fn,params)
    {
        return this._run(true,fn,arguments);
    }

    _run(once,fn,args){
        let c = this._calls;
        let pc = c;
        while(c){
            if(c.f===fn&&c.f.this===fn.this){
                c._stopped = false;
                if(once && !c.once)c.once=true;
                return c;
            }
            pc = c;
            c = c.next;
        }
        let aa = new Array(args.length);
        for(var i = 1;i < args.length;i++) aa = args[i];
        let tr = new TickerRunner(fn,aa,once)
        if(pc){            
            pc.next = tr;
        }else{
            this._calls = tr;
        }
        if(this._stopped)
            this._tick();/**触发机关 */
        return tr;
    }
}

/**
 * ticker为全局共享的逻辑辅助循环类，负责管理运行过程中的持续或者一次性逻辑.
 *
 * @example
 * // 持续运行
 * let renderer = PIXI.ticker;
 * ticker.run(function () {
 *     do something every frame
 * });
 *
 * @example
 * // 一次性运行
 * let renderer = PIXI.ticker;
 * ticker.run_once(function () {
 *     do something once
 * });
 * 
 * @example
 * // 结束持续运行方式1
 * let renderer = PIXI.ticker;
 * var handle = ticker.run(function () {
 *     do something every frame
 *     return true;// 返回`true`结束
 * });
 * 
 * @example
 * // 结束持续运行方式2
 * let renderer = PIXI.ticker;
 * var handle = ticker.run(function () {
 *     do something every frame
 * }); 
 * handle.stop();
 *
 * @type {PIXI.Ticker}
 * @memberof PIXI.ticker
 */
const ticker = new Ticker();
export default ticker;